#include <mips/regdef.h>
#include <sys/syscall.h>

#define ERROR_ESCRITURA -1
#define ERROR_LECTURA -2
#define RES_OK 0
#define ERR_WRITE 1
#define ERR_READ 2
#define ERR_INVALID_CHAR 3
#define ERR_INVALID_PAD 4
#define ERR_PADDING_NOT_EOF 5
#define ERR_EOF 6

	.text
	.align 2	
	.global base64_encode
	.ent	base64_encode
base64_encode:
	.frame	 $fp, 48,  ra
	.set	noreorder
	.cpload	t9
	.set	reorder
	subu	sp, sp, 48
	.cprestore 44
	sw		 $fp, 40(sp)		#Guardo el $fp en el frame
	sw		 ra, 36(sp)			#Guardo el ra en el frame
	move	 $fp,  sp			#Seteo el $fp

	sw		a0, 48($fp)			#a0: fdin
	sw		a1, 52($fp)			#a1: fdout

	sw		s0, 28($fp)			#s0: count
	sw		s1, 24($fp)			#s1: block_count

	move	s0, zero
	move	s1, zero
	sw	 	zero, 20($fp)		#Inicializo el encode_block
	sw	 	zero, 16($fp)		#Inicializo el buffer


COND_while_encode:
	la		a0, 20($fp)		#a0: puntero al encode_block
	lw		a1, 48($fp)		#a1: fdin

	jal		read_from_input

	move	s0, v0
	li		t0, 3
	bne		s0, t0, END_while_encode

BODY_while_encode:
	sw		zero, 16($fp)	#Inicializo el buffer

	la		a0, 16($fp)		#a0: puntero al buffer
	or		t0, zero, zero
	or		t1, zero, zero
	or		t2, zero, zero
	or		a1, zero, zero

	lbu		t0, 20($fp)
	sll		t0, t0, 24
	lbu		t1, 21($fp)
	sll		t1, t1, 16
	lbu		t2, 22($fp)
	sll		t2, t2, 8
	or		a1, t0, t1
	or		a1, a1, t2		#a2: bloque ya leido
	li		a2, 24			#a2: bits a escribir
	lw		a3, 52($fp)		#a3: fdout

	jal		write_buffer

	bnez	v0, error_escritura	#Si hubo un error retorno, devolviendo el valor de error

	addiu	s1, s1, 1

if_1:
	li		t0, 19
	move	t2, s1
	divu	t2, t0			#block_count div 19
	mfhi 	t1				#t1 = s1 % t0

	bnez	t1, end_if1		#if(blocks_count % 19 != 0) salto

	li		v0, SYS_write	# ver dentro de <sys/syscall.h>.
	lw		a0, 52($fp)		# a0: fdout
	la		a1, CRLF       	# a1: Almaceno en a1 el caracter CRLF.
	li		a2, 1	      	# a2: bytes a escribir: 1b.

	syscall

	bne 	 zero, a3, error_escritura	#Si a3 != 0, error de escritura
	li		 t0, 1
	bne 	 v0, t0, error_escritura	#Si v0 != 1, error de escritura

end_if1:
	b		COND_while_encode

END_while_encode:
	li		t0, ERROR_LECTURA
	beq		t0, s0, error_lectura

if_2:
	li		t0, 2
	bne		s0, t0, else_if2

	sw		zero, 16($fp)	#Inicializo el buffer

	la		a0, 16($fp)		#a0: puntero al buffer
	or		t0, zero, zero
	or		t1, zero, zero
	or		a1, zero, zero

	lbu		t0, 20($fp)
	sll		t0, t0, 24
	lbu		t1, 21($fp)
	sll		t1, t1, 16
	or		a1, t0, t1		#a1: bloque ya leido
	li		a2, 18			#a2: bits a escribir
	lw		a3, 52($fp)		#a3: fdout

	jal		write_buffer

	bnez	v0, error_escritura	#Si hubo un error retorno, devolviendo el valor de error

	lw		a0, 52($fp)

	jal		push_padding

	bnez	v0, error_escritura	#Si hubo un error retorno, devolviendo el valor de error


end_if2:
	b	end_ifs

else_if2:

	li		t0, 1
	bne		s0, t0, end_ifs

	sw		zero, 16($fp)	#Inicializo el buffer

	la		a0, 16($fp)		#a0: puntero al buffer

	or		t0, zero, zero
	or		a1, zero, zero

	lbu		t0, 20($fp)
	sll		t0, t0, 24
	move 	a1, t0			#a1: bloque ya leido
	li		a2, 12			#a2: bits a escribir
	lw		a3, 52($fp)		#a3: fdout

	jal		write_buffer

	bnez	v0, error_escritura	#Si hubo un error retorno, devolviendo el valor de error

	lw		a0, 52($fp)

	jal		push_padding

	bnez	v0, error_escritura	#Si hubo un error retorno, devolviendo el valor de error

	jal		push_padding

	bnez	v0, error_escritura	#Si hubo un error retorno, devolviendo el valor de error

end_ifs:

if_3:

	li		t0, 19
	div		s1, t0			#block_count div 19
	mfhi 	t1				#t1 = s1 % t0

	bnez	t1, end_if3		#if(blocks_count % 19 != 0) salto

	li		t0, 3
	move	t2, s0
	divu	t2, t0			#count div 3
	mfhi 	t1				#t1 = s1 % t0

	beq		zero,t1, end_if3		#if(count % 3 == 0) salto

	li		v0, SYS_write	# ver dentro de <sys/syscall.h>.
	lw		a0, 52($fp)		# a0: fdout
	la		a1, CRLF       	# a1: Almaceno en a1 el caracter CRLF.
	li		a2, 1	      	# a2: bytes a escribir: 1b.

	syscall

	bne 	 zero, a3, error_escritura	#Si a3 != 0, error de escritura
	li		 t0, 1
	bne 	 v0, t0, error_escritura	#Si v0 != 1, error de escritura

end_if3:
	li		v0, RES_OK
	b		return_encode_file

error_lectura:
	li		v0, ERR_READ	#Seteo el valor de retorno
	b		return_encode_file

error_escritura:
	li		v0, ERR_WRITE	#Seteo el valor de retorno
	b		return_encode_file

return_encode_file:
	lw		s0, 28($fp)		#Restauro s0
	lw		s1, 24($fp)		#Restauro s1

	lw		$fp, 40(sp)		#Recupero $fp
	lw		ra, 36(sp)		#Recupero ra

	addiu	sp, sp, 48 		#Restauro el stack
	j	ra
	.end	base64_encode

	.global base64_decode
	.ent base64_decode
base64_decode: # 4 SRA, 9 LTA, 4 ABA
	.frame $fp, 68, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 68
	.cprestore 52
	sw $fp, 56(sp)
	move $fp, sp
	sw ra, 60($fp)
	sw a0, 68($fp) # int infd
	sw a1, 72($fp) # int outfd
	# int i: 16($fp)
	# int n: 20($fp)
	# int line: 24($fp)
	# int byte: 28($fp)
	# int aux2: 32($fp)
	# char block[4]: 36($fp)
	# char decoded_block[4]: 40($fp)
	# char aux: 44($fp)
	# ssize_t nread: 48($fp)

# - int i = 0, n = 3, line = 1, byte = 1, aux2;
	li t1, 0
	sw t1, 16($fp)
	li t1, 3
	sw t1, 20($fp)
	li t1, 1
	sw t1, 24($fp)
	li t1, 1
	sw t1, 28($fp)
# - char block[4], decoded_block[4], aux;
	la t1, base64_decode_block
	sw t1, 36($fp)
	la t1, base64_decode_decoded_block
	sw t1, 40($fp)
# - while(nread = my_read(infd, &(block[i]), sizeof(char)))
base64_decode_while_start:
	lw a0, 68($fp)
	lw t1, 36($fp)
	lw t2, 16($fp)
	addu a1, t1, t2
	li a2, 1
	jal my_read
	sw v0, 48($fp)
	beqz v0, base64_decode_while_end
# - if(nread == -1)
	lw t1, 48($fp)
	li t2, -1
	bne t1, t2, base64_decode_while_C1
# - return ERR_READ;
	li v0, ERR_READ
	j return_base64_decode
base64_decode_while_C1:
# - if(block[i] == '\n')
	lw t1, 36($fp)
	lw t2, 16($fp)
	addu t1, t1, t2
	lb t2, 0(t1)
	li t3, '\n'
	bne t2, t3, base64_decode_while_C2_else
# - line++;
	lw t1, 24($fp)
	addi t1, t1, 1
	sw t1, 24($fp)
# - byte = 1;
	li t1, 1
	sw t1, 28($fp)
	j base64_decode_while_C2
base64_decode_while_C2_else:
# - aux = base64_to_ascii(block[i]);
	lw t1, 36($fp)
	lw t2, 16($fp)
	addu t1, t1, t2
	lb a0, 0(t1)
	jal base64_to_ascii
	sb v0, 44($fp)
# - if(aux == -1)
	lb t1, 44($fp)
	li t2, -1
	bne t1, t2, base64_decode_while_C2_else_C1
# - return ERR_INVALID_CHAR;
	li v0, ERR_INVALID_CHAR
	j return_base64_decode
base64_decode_while_C2_else_C1:
# - if(n != 3)
	lw t1, 20($fp)
	li t2, 3
	beq t1, t2, base64_decode_while_C2_else_C2
# - return ERR_PADDING_NOT_EOF;
	li v0, ERR_PADDING_NOT_EOF
	j return_base64_decode
base64_decode_while_C2_else_C2:
# - i++;
	lw t1, 16($fp)
	addi t1, t1, 1
	sw t1, 16($fp)
# - byte++;
	lw t1, 28($fp)
	addi t1, t1, 1
	sw t1, 28($fp)
base64_decode_while_C2:
# - if(i == 4)
	lw t1, 16($fp)
	li t2, 4
	bne t1, t2, base64_decode_while_C3
# - n = decode_block(block, decoded_block);
	lw a0, 36($fp)
	lw a1, 40($fp)
	jal decode_block
	sw v0, 20($fp)
# - if(n < 0)
	lw t1, 20($fp)
	li t2, 0
	bge t1, t2, base64_decode_while_C3_C1
# - return ERR_INVALID_PAD;
	li v0, ERR_INVALID_PAD
	j return_base64_decode
base64_decode_while_C3_C1:
# - aux2 = my_write(outfd, decoded_block, sizeof(char) * n);
	lw a0, 72($fp)
	lw a1, 40($fp)
	lw a2, 20($fp)
	jal my_write
	sw v0, 32($fp)
# - if(aux2 != sizeof(char) * n)
	lw t1, 32($fp)
	lw t2, 20($fp)
	beq t1, t2, base64_decode_while_C3_C2
# - return ERR_WRITE;
	li v0, ERR_WRITE
	j return_base64_decode
base64_decode_while_C3_C2:
# - i = 0;
	li t1, 0
	sw t1, 16($fp)
base64_decode_while_C3:
# --- end while
	j base64_decode_while_start
base64_decode_while_end:
# - if(i)
	lw t1, 16($fp)
	beqz t1, base64_decode_C1
# - return ERR_EOF;
	li v0, ERR_EOF
	j return_base64_decode
base64_decode_C1:
# - return RES_OK;
	li v0, RES_OK
return_base64_decode:
	lw ra, 60(sp)
	lw $fp, 56(sp)
	addiu sp, sp, 68
	jr ra
	.end base64_decode

	.data
	.align 2
	.space 4
base64_decode_block:
	.space 4
base64_decode_decoded_block:
	.space 4

	.rdata
	.align 2
PADDING:
	.asciiz "="
CRLF:
	.asciiz "\n"

	.global errmsg
errmsg:
	.word errmsg_0, errmsg_1, errmsg_2, errmsg_3, errmsg_4, errmsg_5
	.size errmsg, 24
	.align 0
errmsg_0:
	.asciiz "No se pudo escribir en el archivo"
errmsg_1:
	.asciiz "No se pudo leer del archivo"
errmsg_2:
	.asciiz "Caracter invalido"
errmsg_3:
	.asciiz "Padding invalido"
errmsg_4:
	.asciiz "Padding antes de fin de archivo"
errmsg_5:
	.asciiz "Fin de archivo inesperado"
